package org.pug.generator;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import org.apache.commons.lang3.StringUtils;
import org.pug.generator.anno.PugDoc;
import org.pug.generator.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.*;
import java.nio.charset.StandardCharsets;
import java.text.Collator;
import java.util.*;

/**
 * @author 飞哥
 * @Title: 学相伴出品
 * @Description: 飞哥B站地址：https://space.bilibili.com/490711252
 * 记得关注和三连哦！
 * @Description: 我们有一个学习网站：https://www.kuangstudy.com
 * @date 2022-05-23$ 14:24$
 */
public class GeneratorService {

    @Autowired
    private GeneratorProperties generatorProperties;

    public void createApi(String controller, String outPath, String filename) {
        try {
            // 1 获取所有的类
            Set<Class> classes = ClassScaner.scan(controller, RestController.class);
            // 2 把包下所有的类进行遍历
            List<Map<String, Object>> objectList = new ArrayList<>();
            for (Class aClass : classes) {
                // 获取每隔类的所有的方法，方法对应的参数，返回值，注解等信息
                TreeMap<String, Object> apiList = createApiDoc(aClass);
                objectList.add(apiList);
            }
            // 3: 创建在线文档
            create(objectList, pugGlobalConfig, outPath, filename);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    Comparator comparator = Collator.getInstance(Locale.CHINA);
    // 全局设置
    PugGlobalConfig pugGlobalConfig = PugGlobalConfig.builder()
            .isoverride()
            .author("yykk")
            .title("轮播图")
            .gitlink("前台代码 git clone https://gitee.com/kekesam/kuangstudy-pug-ui.git")
            .url("后台代码 git clone https://gitee.com/kekesam/kuangstudy-pug-parent.git")
            .commentDate("yyyy-MM-dd HH:mm:ss")
            .version("1.0.0")
            .port("8877")
            .apititle("学相伴在线接口文档")
            .apidesc("学相伴在线接口文档我是一个描述")
            .apimemebers("yykk,kuangshen")
            .apiversion("1.0.0")
            .apiurl("http://localhost:8877/admin")
            .build();

    // 初始化数据源
    PugDataSourceConfig dataSourceConfig = PugDataSourceConfig.builder()
            .url("jdbc:mysql://127.0.0.1:3306/kss-zixun-db?serverTimezone=GMT%2b8&useUnicode=true&characterEncoding=utf-8&useSSL=false")
            .username("root")
            .password("mkxiaoer").build();


    public TreeMap<String, Object> createApiDoc(Class<?> clz) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

        // 创建一个map容器，去装没一个class对象的方法信息
        TreeMap<String, Object> map = new TreeMap<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return comparator.compare(o1, o2);
            }
        });

        map.put("name", clz.getName());
        map.put("sname", clz.getSimpleName());
        PugDoc mpugDoc = clz.getAnnotation(PugDoc.class);
        if (mpugDoc != null) {
            map.put("title", mpugDoc.name());
            map.put("tabname", mpugDoc.tabname());
            map.put("model", mpugDoc.model());
            map.put("desc", mpugDoc.desc());
        } else {
            map.put("title", clz.getSimpleName());
            map.put("desc", clz.getSimpleName());
            map.put("tabname", "");
            map.put("model", "");
        }


        // 循环类的所有请求方法了
        List<TreeMap<String, Object>> mapList = new ArrayList<>();
        // 反射获取当前类中所有的方法
        Method[] declaredMethods = clz.getDeclaredMethods();
        // 遍历所有的方法
        for (Method declaredMethod : declaredMethods) {
            // 开始遍历所有的方法，准备开始装配方法的信息
            TreeMap<String, Object> objectMap = new TreeMap<>(new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return comparator.compare(o1, o2);
                }
            });

            // 方法信息
            objectMap.put("methodname", declaredMethod.getName());//findBannerList
            // 返回值
            objectMap.put("returnName", declaredMethod.getReturnType().getName()); //BannerBo
            objectMap.put("returnSName", declaredMethod.getReturnType().getSimpleName());//BannerBo.class

            // 返回值：基础数据类型，封装数据类型，类类型（bean,List,Map Page）
            if (declaredMethod.getReturnType() == Integer.class
                    || declaredMethod.getReturnType() == Long.class
                    || declaredMethod.getReturnType() == byte.class
                    || declaredMethod.getReturnType() == short.class
                    || declaredMethod.getReturnType() == int.class
                    || declaredMethod.getReturnType() == long.class
                    || declaredMethod.getReturnType() == float.class
                    || declaredMethod.getReturnType() == double.class
                    || declaredMethod.getReturnType() == char.class
                    || declaredMethod.getReturnType() == Byte.class
                    || declaredMethod.getReturnType() == Short.class
                    || declaredMethod.getReturnType() == Float.class
                    || declaredMethod.getReturnType() == Double.class
                    || declaredMethod.getReturnType() == Long[].class
                    || declaredMethod.getReturnType() == Integer[].class
                    || declaredMethod.getReturnType() == Byte[].class
                    || declaredMethod.getReturnType() == Double[].class
                    || declaredMethod.getReturnType() == Short[].class
            ) {
                objectMap.put("returnList", JsonUtil.obj2StringPretty(R.success("1")));
            } else if (declaredMethod.getReturnType() == String.class || declaredMethod.getReturnType() == String[].class) {
                objectMap.put("returnList", JsonUtil.obj2StringPretty(R.success("success")));
            } else if (
                declaredMethod.getReturnType() == boolean.class || declaredMethod.getReturnType() == boolean[].class ||
                            declaredMethod.getReturnType() == Boolean.class || declaredMethod.getReturnType() == Boolean[].class
            ) {
                objectMap.put("returnList", JsonUtil.obj2StringPretty(R.success("true")));
            } else {
                //其他都是类类型
                Type genericReturnType = declaredMethod.getGenericReturnType();
                Type tactuallyTypeArgument = null;
                Type ownerType = null;
                if (genericReturnType instanceof ParameterizedType) { // List<UserBo>
                    ownerType = ((ParameterizedType) genericReturnType).getRawType();
                    Type[] actuallyTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                    for (Type actuallyTypeArgument : actuallyTypeArguments) {
                        tactuallyTypeArgument = actuallyTypeArgument;
                    }
                } else { // UserBo
                    tactuallyTypeArgument = genericReturnType;
                    ownerType = genericReturnType;
                }

                // tactuallyTypeArgument.getTypeName() 得到泛型的具体的类型，实例化，
                if (tactuallyTypeArgument != null) {
                    Object obj = Class.forName(tactuallyTypeArgument.getTypeName()).newInstance();
                    if (ownerType == List.class) { // 应对List<BO>
                        List<Object> list = new ArrayList<>();
                        list.add(obj);
                        objectMap.put("returnList", JsonUtil.obj2StringPretty(R.success(list)));
                    } else if (ownerType == IPage.class) {// 应对IPage<BO>
                        List<Object> list = new ArrayList<>();
                        list.add(obj);
                        IPage page = new Page(1, 10);
                        page.setRecords(list);
                        objectMap.put("returnList", JsonUtil.obj2StringPretty(R.success(page)));
                    } else {
                        // 应对BO
                        objectMap.put("returnList", JsonUtil.obj2StringPretty(R.success(obj)));
                    }
                } else {
                    // 处理void的方法
                    objectMap.put("returnList", JsonUtil.obj2StringPretty(R.success("")));
                }
            }

            // 参数部分
            Parameter[] parameters = declaredMethod.getParameters();
            List<Map> parameterTypesMap = new ArrayList<>();
            // 遍历每个参数
            for (Parameter parameter : parameters) {
                // 获取参数类型
                Class<?> parameterType = parameter.getType();
                // 开始判断类型是不是基础数据类型和String
                if (parameterType == String.class || parameterType == Integer.class
                        || parameterType == Long.class
                        || parameterType == Byte.class
                        || parameterType == Short.class
                        || parameterType == Float.class
                        || parameterType == Double.class
                        || parameterType == Boolean.class
                        || parameterType == String[].class
                        || parameterType == Long[].class
                        || parameterType == Integer[].class
                        || parameterType == Byte[].class
                        || parameterType == Double[].class
                        || parameterType == Short[].class
                        || parameterType == Boolean[].class
                ) {

                    // 准备一个Map装载每个参数信息
                    Map<String, Object> paramterMap = new HashMap<>();

                    paramterMap.put("parameterName", parameter.getName());//java.lang.String
                    paramterMap.put("parameterSName", parameterType.getSimpleName());//String

                    List<Map<String, Object>> fieldMaps = new ArrayList<>();
                    // 有路由PathVariable 和 @RequestParam
                    PathVariable annotation1 = parameter.getAnnotation(PathVariable.class);
                    RequestParam annotation2 = parameter.getAnnotation(RequestParam.class);
                    Boolean requried = false;
                    String defaultV = "";
                    String ann = "";
                    String title = "";
                    // 判断是不是PathVariable
                    if (annotation1 != null) {
                        requried = true;
                        ann = "@PathVariable";
                        title = annotation1.value();

                    }

                    // 判断是不是@RequestParam
                    if ((annotation2 != null && annotation2.required())) {
                        requried = true;
                    }

                    // 判断是不是@RequestParam,并且你指定了默认值
                    if (annotation2 != null) {
                        defaultV = annotation2.defaultValue();
                        ann = "@RequestParam";
                        title = annotation2.value();
                    }

                    Map<String, Object> cmap = new HashMap<>();
                    cmap.put("name", title);
                    cmap.put("title", title);
                    cmap.put("desc", "");
                    cmap.put("default", defaultV);
                    cmap.put("required", requried ? "Y" : "N");
                    cmap.put("type", parameterType.getTypeName());
                    cmap.put("stype", parameterType.getTypeName().replaceAll("java\\.lang\\.", ""));
                    cmap.put("anno", ann);
                    fieldMaps.add(cmap);
                    paramterMap.put("parameterFields", fieldMaps);
                    parameterTypesMap.add(paramterMap);
                } else {
                    if (parameterType != HttpServletRequest.class &&
                            parameterType != HttpServletResponse.class &&
                            parameterType != HttpSession.class &&
                            parameterType != Model.class &&
                            parameterType != ModelAttribute.class &&
                            parameterType != ModelAndView.class &&
                            parameterType != ModelMap.class) {
                        Map<String, Object> paramterMap = new HashMap<>();
                        paramterMap.put("parameterName", parameterType.getName());
                        paramterMap.put("parameterSName", parameterType.getSimpleName());
                        paramterMap.put("parameterFields", getClassFields(parameterType));
                        parameterTypesMap.add(paramterMap);
                    }
                }
            }


            // 解析的方法放入到方法Map中
            objectMap.put("parameterList", parameterTypesMap);


            // method方法的注解部分
            PostMapping annotation1 = declaredMethod.getAnnotation(PostMapping.class);
            GetMapping annotation2 = declaredMethod.getAnnotation(GetMapping.class);
            DeleteMapping annotation3 = declaredMethod.getAnnotation(DeleteMapping.class);
            PutMapping annotation4 = declaredMethod.getAnnotation(PutMapping.class);
            RequestMapping annotation5 = declaredMethod.getAnnotation(RequestMapping.class);

            PugDoc pugDoc = declaredMethod.getAnnotation(PugDoc.class);
            // 是不是文档注解
            if (pugDoc != null) {
                objectMap.put("title", pugDoc.name());
                objectMap.put("desc", pugDoc.desc());
                objectMap.put("token", pugDoc.token());
            } else {
                objectMap.put("title", declaredMethod.getName());
                objectMap.put("desc", declaredMethod.getName());
            }

            // 注解是不是请求注解
            if (annotation1 != null) {
                objectMap.put("httpType", "POST");
                objectMap.put("httpTypes", "POST".toLowerCase());
                objectMap.put("url", annotation1.value()[0]);
            } else if (annotation2 != null) {
                objectMap.put("httpType", "GET");
                objectMap.put("httpTypes", "GET".toLowerCase());
                objectMap.put("url", annotation2.value()[0]);
            } else if (annotation3 != null) {
                objectMap.put("httpType", "DELETE");
                objectMap.put("httpTypes", "DELETE".toLowerCase());
                objectMap.put("url", annotation3.value()[0]);
            } else if (annotation4 != null) {
                objectMap.put("httpType", "PUT");
                objectMap.put("httpTypes", "PUT".toLowerCase());
                objectMap.put("url", annotation4.value()[0]);
            }else{
                objectMap.put("httpType", "ALL");
                objectMap.put("httpTypes", "ALL".toLowerCase());
                objectMap.put("url", annotation5.value()[0]);
            }

            mapList.add(objectMap);
        }

        // 数据库信息
        // 获取数据表信息
        String tabname = String.valueOf(map.get("tabname"));
        if (StringUtils.isNotEmpty(tabname)) {
            PugDataTableSourceConfig pugDataTableSourceConfig = PugDataTableSourceConfig.builder()
                    .tablename(tabname).buildTableInfo(dataSourceConfig);
            map.put("fields", pugDataTableSourceConfig.getTableInfos());
        } else {
            map.put("fields", new ArrayList<>());
        }

        map.put("methodInfos", mapList);

        return map;
    }

    // jsp /freemaker /thymeleaf /vue
    //数据 ---- 模板 --freemaker --页面
    // Model / ModelMap /ModelAndView -- freemaker --Map
    public static void create(List<Map<String, Object>> mapList,
                              PugGlobalConfig pugGlobalConfig, String outPath, String fileName) {
        try {
            Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
            // 指定模板文件从何处加载的数据源，这里设置成一个文件目录。
            File file = new ClassPathResource("/templates").getFile();
            cfg.setDirectoryForTemplateLoading(file);
            // 指定模板如何检索数据模型，这是一个高级的主题了… // 但先可以这么来用：
            cfg.setObjectWrapper(new DefaultObjectWrapper(Configuration.VERSION_2_3_28));
            // 创建根哈希表
            Map root = new HashMap();
            // 在根中放入字符串"user"
            root.put("author", pugGlobalConfig.getAuthor());
            root.put("datetime", pugGlobalConfig.getDatetime());
            root.put("title", pugGlobalConfig.getTitle());
            root.put("burl", pugGlobalConfig.getUrl());
            root.put("version", pugGlobalConfig.getVersion());
            root.put("bootversion", pugGlobalConfig.getBootversion());
            root.put("gitlink", pugGlobalConfig.getGitlink());
            root.put("apititle", pugGlobalConfig.getApititle());
            root.put("apidesc", pugGlobalConfig.getApidesc());
            root.put("apiurl", pugGlobalConfig.getApiurl());
            root.put("apiversion", pugGlobalConfig.getApiversion());
            root.put("apimemebers", pugGlobalConfig.getApimemebers());
            root.put("rootPath", "com.pug.zixun");


            root.put("mapList", mapList);

            Template temp = cfg.getTemplate("/api/template2.html");
            // 指定最终渲染的页面存储的位置
            File targetFile = new File(outPath, fileName);
            Writer out = new OutputStreamWriter(new FileOutputStream(targetFile), StandardCharsets.UTF_8);
            // freemaker的模板渲染替换
            temp.process(root, out);
            out.flush();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    private List<Map<String, Object>> getClassFields(Class<?> clz) {
        List<Map<String, Object>> fieldMaps = new ArrayList<>();
        Field[] declaredFields = clz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            PugDoc pugDoc = declaredField.getAnnotation(PugDoc.class);
            Map<String, Object> map = new HashMap<>();
            if (pugDoc != null) {
                map.put("title", pugDoc.name());
                map.put("desc", pugDoc.desc());
                map.put("required", pugDoc.required() ? "Y" : "N");
            } else {
                map.put("title", declaredField.getName());
                map.put("desc", declaredField.getName());
                map.put("required", "N");
            }

            map.put("default", "");
            map.put("anno", declaredField.getAnnotatedType().getType().getTypeName());
            map.put("name", declaredField.getName());
            map.put("type", declaredField.getGenericType().getTypeName());
            map.put("stype", declaredField.getGenericType().getTypeName().replaceAll("java\\.lang\\.", ""));
            fieldMaps.add(map);
        }
        return fieldMaps;
    }
}
